webapi_top

PokeAPIでポケモン図鑑を作ろう

今日の目標!

今日作るもの

今回は、ポケモンの名前を入力すると、タイプや重さ、身長を教えてくれるプログラムを動かすよ。

検索するポケモンの名前:ピカチュウ
名前: pikachu
タイプ1: electric
重さ: 60
身長: 4

この情報は、プログラムの中に全部書いてあるわけではない。

Pythonがインターネットの向こうにある PokeAPI に問い合わせて、必要な情報をもらってくる。

ポケモンの名前を入力
        ↓
PokeAPIへ問い合わせる
        ↓
ポケモンの情報が返ってくる
        ↓
必要な情報を画面に表示

これまでに勉強した関数や辞書型が、インターネット上のサービスを使うときにどう役立つのか見ていこう!

まずは完成コードを動かそう

今回使うファイルは、この2つ。

1_webAPI
├── pokemon_api.py
└── pokemon_name_map.json

requestsをインストールしよう

今回のプログラムでは、APIへ問い合わせるために requests という外部ライブラリを使うよ

VS Codeでターミナルを開き、次のコマンドを実行しよう。

pip install requests

エラーが出ずにインストールが終わればOK!

プログラムを実行しよう

ターミナルで、次のコマンドを実行する。

python pokemon_api.py

検索するポケモンの名前: と表示されたら、ピカチュウ と入力してEnterキーを押そう。

別のポケモンも試してみてね。プログラムを終わらせるときは、終了 と入力する。

完成コード

import requests
from pathlib import Path
import json

# 日本語→英語対応表ファイルの読み込み
json_path = Path(__file__).with_name("pokemon_name_map.json")
with json_path.open(encoding="utf-8") as f:
    pokemon_dict = json.load(f)


def get_pokemon_info(pokemon):
    types = []

    # ポケモンの名前をURLに加えてリクエストを送る
    url = f"https://pokeapi.co/api/v2/pokemon/{pokemon_dict[pokemon]}"

    # レスポンスをjson形式で受け取る
    response = requests.get(url)
    # json形式のファイルを辞書型として読み込む
    data = response.json()

    for i in data['types']:
        types.append(i["type"]["name"])

    result = {"name": data["name"],
              "weight": data["weight"],
              "height": data["height"],
              "types": types}

    return result

while True:
    pokemon = input("検索するポケモンの名前:")
    if pokemon == "終了":
        break
    pokemon_info = get_pokemon_info(pokemon)

    print(f"名前: {pokemon_info["name"]}")
    for i in range(len(pokemon_info["types"])):
        print(f"タイプ{i + 1}: {pokemon_info["types"][i]}")
    print(f"重さ: {pokemon_info['weight']}")
    print(f"高さ: {pokemon_info['height']}")

まずは関数のおさらい

いきなりAPIの話へ行く前に、これまで勉強した関数を思い出そう。

今回のコードには、こんな関数がある。

def get_pokemon_info(pokemon):
    # ポケモンの情報を取得する処理
    return result

関数は、いくつかの処理を1つの機能としてまとめたものだったね。

今回の関数名は get_pokemon_info()。英語で get は「手に入れる」、pokemon_info は「ポケモンの情報」という意味なので、「ポケモンの情報を手に入れる関数」という名前になっている。
※info : information(インフォメーション:情報)の略

引数

def get_pokemon_info(pokemon):

pokemon の部分が 引数(ひきすう)

get_pokemon_info("ピカチュウ")

この場合、関数の中では pokemon"ピカチュウ" が入っているものとして使える。

get_pokemon_info("ピカチュウ")
                 ↓
          pokemon = "ピカチュウ"

ポケモン名を引数で渡せるから、同じ関数でピカチュウもリザードンも検索できるわけだ!

戻り値

関数の最後には return がある。

return result

return は、関数で作った結果を呼び出し元へ戻すための命令だったね。

pokemon_info = get_pokemon_info("ピカチュウ")

このコードでは、get_pokemon_info() が返した結果を pokemon_info という変数に入れている。

「ピカチュウ」という引数を関数へ渡す
                ↓
関数がピカチュウの情報を集める
                ↓
集めた情報を戻り値として返す
                ↓
pokemon_infoに入る

引数を投げたら、戻り値が返ってくる。関数とデータをキャッチボールしているイメージだね。

辞書型もおさらい

今回のプログラムでは、ポケモンの情報を 辞書型 にまとめている。

辞書型は、キーバリュー をセットにしてデータを保存するもの。

dict_review

左側の "name""weight" がキー。右側の "pikachu"60 がバリュー。

辞書からデータを取り出す

辞書型のデータは、キーを使って取り出す。

print(pokemon["name"])
# 結果:pikachu

print(pokemon["weight"])
# 結果:60

リストではインデックス番号を使ったけど、辞書型ではキーを使う。呼び出すときは、どちらも大かっこ [] を使うので、ちょっとまぎらわしいよね。

辞書の中に辞書やリストがある場合

PokeAPIから受け取るデータには、辞書の中に別の辞書が入っていることがある。これによって、複雑な情報を一つの変数にまとめて置くことができる。

type_data = {
    "type": {
        "name": "electric",
        "url": "https://pokeapi.co/api/v2/type/13/",
    }
}

この中から electric を取り出すには、外側から順番にキーを書く。

print(type_data["type"]["name"])
# 結果:electric

大きな箱から小さな箱を順番に開けていく感じだね。

ポケモンはタイプを2つ持っていることがあるので、タイプの情報はリストになっている。

"types": [
    {
        "type": {
            "name": "electric"
        }
    }
]

サンプルコードでは、forを使ってリストの中身を1つずつ取り出している。

for i in data["types"]:
    types.append(i["type"]["name"])

データがややこしく見えたときは、まず「今見ているのは辞書?リスト?」と考えるとわかりやすくなるよ。

APIってなんだ?

APIは、別のサービスの機能やデータを、プログラムから使うための「窓口」や「やり取りのルール」のこと。

※API:Application Programming Interfaceの略

たとえば、レストランで料理を注文するとき、お客さんが厨房へ勝手に入って冷蔵庫を開けることはないよね。

メニュー表があると、メニューの値段や写真、カロリーなどが書いてあって、いちいちお客さんから聞かれることもないよね

APIも、だいたい同じ。

api_ramen

今回の場合、Pythonファイルはポケモンの情報を知りたい。でも、そのために任天堂のサーバーへ勝手に入って情報を盗み出すのは、もちろんダメ。

任天堂側が「ポケモンの情報を知りたい人はここを見てね!」という、メニュー表にあたるAPIを用意してくれているから、そこへお願いしに行こうってわけ。

リクエストとレスポンス

APIを使うときは、リクエストレスポンス という2つの言葉がよく出てくる。

リクエスト:PythonからAPIへ送るお願い
レスポンス:APIからPythonへ返ってくる結果

今回のプログラムなら、こんなやり取りになる。

リクエスト
「ピカチュウの情報をください!」
        ↓
PokeAPI
        ↓
レスポンス
「名前はpikachu、重さは60、タイプはelectric……」

つまりAPIを使うときは、次の2つが必要になる。

  1. どこへ、どんな情報がほしいとお願いするのか
  2. 返ってきた情報を、プログラムでどうやって読むのか

今回のPokeAPIでは、リクエスト先を指定するために URL を使い、レスポンスのデータを返す形式として JSON が使われている。

Python
  │
  │ URLを指定してリクエスト
  ▼
PokeAPI
  │
  │ JSON形式でレスポンス
  ▼
Python

それぞれ、もう少しくわしく見ていこう。

リクエストを送る:URLのしくみ

みんながウェブサイトにアクセスするとき、ウェブサイトにアクセスするためにURLをクリックしたりすることあるよね。実はあの文字には、結構いろんなことが書いてある。

例えば、youtubeで動画を検索するとき、いろんな動画がある中から、youtuberの名前や、気になっているワードで検索したりすることあるよね。 それらを指定してURLにすると、こんな感じになる。

url

URLには他にもいろんな情報が含まれている場合があって、例えばショッピングサイトなら、
・検索ワード
・値段はどのくらいなのか
・どういう並び順で表示するか
などの情報を渡すと、自分にピッタリの商品を表示してくれたりするわけだ。

今回のURLの仕組みを見てみよう。

https://pokeapi.co/api/v2/pokemon/pikachu
URLの部分 意味
https://pokeapi.co PokeAPIのサーバー
/api/v2 APIのバージョン2
/pokemon ポケモンの基本情報を取得する窓口
/pikachu 取得したいポケモンの名前

今回入力するのは日本語の名前だけど、PokeAPIへは英語名を送る必要がある。 pokemon_name_map.json がポケモンの日本語名と英語名の対応表になっているから、それを使おう。

{
  "ピカチュウ": "pikachu",
  "ライチュウ": "raichu"
}
url = f"https://pokeapi.co/api/v2/pokemon/{pokemon_dict[pokemon]}"

ピカチュウ と入力した場合、辞書から pikachu を取り出してURLにつなげるわけだ!

作ったURLへ実際にリクエストを送るのが、このコード。

response = requests.get(url)

最初にインストールした requests ライブラリは、文字通り、URLに対してリクエストを送っているわけだ。

レスポンスの内容をみてみよう:JSON(ジェイソン)形式

リクエストを受け取ったPokeAPIは、ポケモンの情報をレスポンスとして返してくれる。

レスポンスを返すときも、おたがいに「どんな形でデータを並べるか」というルールが必要になる。

人によってバラバラの書き方で返ってきたら、プログラムはどこに名前や重さが書いてあるのかわからないからね。

PokeAPIのレスポンスでは、JSON(ジェイソン)形式 が使われている。

{
  "name": "pikachu",
  "weight": 60,
  "height": 4,
  "is_default": true,
  "held_item": null,
  "types": ["electric"]
}

この形、どこかで見覚えがない?
そう、Pythonの辞書型そのもの。

JSON Python
オブジェクト {} 辞書型 dict
配列 [] リスト型 list
文字列 文字列型 str
数字 整数型 int、小数型 float
true / false True / False
null None

見た目は似ているけど、JSONとPythonの辞書型は同じものではない。

JSONは、いろいろなプログラミング言語でやり取りできるようにしたデータの書き方。受け取ったあとは、Pythonで使える辞書型やリスト型へ変換する必要がある。

APIから返ってきたレスポンスは、response に入る。でも、JSON形式のままではPythonの辞書型として使えない。

そこで、レスポンスのJSONをPythonのデータ型へ変換する。

data = response.json()

これで、Pythonの中で辞書型として扱えるようになったので、中のデータを取り出して見てみよう。

data["name"]
data["weight"]
data["height"]

レスポンスのステータスコード

インターネットを見ているとき、こういう表示を見たことない?

status_code

レスポンスには、リクエストがちゃんと成功したかどうかを表す ステータスコード も入っている。

ステータスコード 意味
200 成功!
404 指定したデータが見つからない
500 APIのサーバー側で問題が起きた

確認したいときは、次のコードで表示できる。

インターネットでいろいろ調べているときにこれらを見ても、単にウェブサイトを作った人がファイルを削除したからかもしれないよ!という場合が多いから、あわてないように!!😏

コードの流れを見てみよう

ここまでの話を、pokemon_api.py のコードとつなげて見ていこう。

①日本語名と英語名の対応表を読み込む

json_path = Path(__file__).with_name("pokemon_name_map.json")
with json_path.open(encoding="utf-8") as f:
    pokemon_dict = json.load(f)

pokemon_name_map.json を読み込み、Pythonの辞書型として pokemon_dict に入れている。

json.load(f) は、ファイルに保存されたJSONを読み込む命令だよ。

②入力された名前を関数へ渡す

pokemon = input("検索するポケモンの名前:")
pokemon_info = get_pokemon_info(pokemon)

input() で入力した日本語名を、get_pokemon_info() の引数へ渡す。

終了 と入力されたときは、breakwhile の無限ループを終わらせる。

③APIのURLを作る

url = f"https://pokeapi.co/api/v2/pokemon/{pokemon_dict[pokemon]}"

日本語名を英語名へ変換し、PokeAPIのURLへつなげている。こういうところでf記法が活躍するね。

④リクエストを送ってレスポンスを受け取る

response = requests.get(url)
data = response.json()

1行目でリクエストを送り、2行目でレスポンスのJSONをPythonの辞書型へ変換する。

たった2行だけど、インターネットの向こうにあるサービスとデータをやり取りしている。急にプログラミングが大がかりになってきた感じがするね!

⑤必要な情報だけを辞書へまとめる

result = {
    "name": data["name"],
    "weight": data["weight"],
    "height": data["height"],
    "types": types,
}

APIから返ってくるJSONには、技、能力値、画像のURLなど、たくさんの情報が入っている。

今回は、その中から名前、重さ、身長、タイプだけを選んで、新しい辞書へまとめている。全部のデータを使う必要はない。必要なものだけ取り出せばOK!

PokeAPIの weight は100グラム単位、height は10センチメートル単位。たとえばピカチュウの weight が60なら6.0kg、height が4なら0.4mという意味だよ。

⑥戻り値を表示する

関数から返された辞書を、キーを使って表示する。

print(f"名前: {pokemon_info['name']}")

for i in range(len(pokemon_info["types"])):
    print(f"タイプ{i + 1}: {pokemon_info['types'][i]}")

print(f"重さ: {pokemon_info['weight']}")
print(f"身長: {pokemon_info['height']}")

これで、入力、APIへのリクエスト、レスポンスの受け取り、画面への表示が全部つながった!

ブラウザでもJSONを見てみよう

次のURLをブラウザで開いてみよう。

https://pokeapi.co/api/v2/pokemon/pikachu

文字がものすごくたくさん出てくるけど、びっくりしなくて大丈夫。

ブラウザの検索機能を使って、nameweightheighttypes を探してみよう。

プログラムは、この大量のJSONから必要な部分だけを見つけて使っているわけだね。

最後のチャレンジ!種族の情報も取ってこよう

ここまで使ってきたのは、pokemon というエンドポイント。

PokeAPIには、ポケモンの「種族」に関する情報を取得する pokemon-species という別のエンドポイントもある。

基本情報
https://pokeapi.co/api/v2/pokemon/pikachu

種族情報
https://pokeapi.co/api/v2/pokemon-species/pikachu

pokemon-species からは、捕まえやすさ、色、初登場した世代、伝説のポケモンかどうかなどを取得できる。

▶ Pokemon Species公式ドキュメント

英語で難しそうに見えるけど、全部読む必要はないよ。表に書かれているキーの名前と、返ってくるJSONの例に注目しよう。

取り出せる情報の例

キー 情報 取り出し方
capture_rate 基本の捕まえやすさ。最大255で、大きいほど捕まえやすい data["capture_rate"]
base_happiness 捕まえたときの基本のなつき度 data["base_happiness"]
is_baby ベイビィポケモンか data["is_baby"]
is_legendary 伝説のポケモンか data["is_legendary"]
is_mythical 幻のポケモンか data["is_mythical"]
color 色の分類 data["color"]["name"]
generation 初登場した世代 data["generation"]["name"]

ミッション

公式ドキュメントを参考にして、好きな種族情報を 2つ以上 取得しよう。

次の関数の result を完成させてね。

def get_pokemon_species_info(pokemon):
    english_name = pokemon_dict[pokemon]
    url = f"https://pokeapi.co/api/v2/pokemon-species/{english_name}"

    response = requests.get(url)
    data = response.json()

    result = {
        # 公式ドキュメントを見て、取得したい情報を追加しよう
    }

    return result

関数を呼び出して、取得できたか確認する。

species_info = get_pokemon_species_info("ピカチュウ")
print(species_info)

ヒント

数字や TrueFalse がそのまま入っているデータは、キーを1つ指定すれば取り出せる。

data["capture_rate"]
data["is_legendary"]

colorgeneration のバリューは辞書型。中にある name を取り出そう。

data["color"]["name"]
data["generation"]["name"]

完成例

def get_pokemon_species_info(pokemon):
    english_name = pokemon_dict[pokemon]
    url = f"https://pokeapi.co/api/v2/pokemon-species/{english_name}"

    response = requests.get(url)
    data = response.json()

    result = {
        "capture_rate": data["capture_rate"],
        "is_legendary": data["is_legendary"],
        "color": data["color"]["name"],
        "generation": data["generation"]["name"],
    }

    return result


species_info = get_pokemon_species_info("ピカチュウ")
print(f"捕まえやすさ: {species_info['capture_rate']}")
print(f"伝説のポケモン: {species_info['is_legendary']}")
print(f"色: {species_info['color']}")
print(f"初登場した世代: {species_info['generation']}")

ピカチュウで成功したら、別のポケモンでも試してみよう。ポケモンによって結果がどう変わるか比べてみると、おもしろいかも!

うまく動かないとき

ModuleNotFoundError: No module named 'requests' と表示される

requests がインストールされていない。ターミナルで次のコマンドを実行しよう。

pip install requests

ポケモン名を入力するとエラーになる

対応表に入っていない名前や、間違った名前を入力すると KeyError が出ることがある。

APIのところでエラーになる

自分のコードが正しくても、API側の問題で一時的に動かないこともある。何回も連打せず、先生に教えてね。

今日のまとめ

最初は文字だらけに見えたJSONも、辞書型やリスト型だと考えると、今まで勉強した方法でデータを取り出せたね。

APIを使えるようになると、天気、地図、ゲーム、AIなど、インターネット上のいろいろなサービスとPythonをつなげられる。

Pythonだけで全部作らなくても、世界中の便利なサービスの力を借りられるわけだ!


参考ページ